package com.dragon.backstage.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dragon.backstage.convert.MenuConvert;
import com.dragon.backstage.domain.param.menu.MenuAddParam;
import com.dragon.backstage.domain.param.menu.MenuChangeStatusParam;
import com.dragon.backstage.domain.param.menu.MenuChangeVisibleParam;
import com.dragon.backstage.domain.param.menu.MenuEditParam;
import com.dragon.backstage.domain.param.menu.MenuParam;
import com.dragon.backstage.domain.vo.menu.MenuAsyncPermissionVo;
import com.dragon.backstage.domain.vo.menu.MenuAsyncRouteVo;
import com.dragon.backstage.domain.vo.menu.MenuDetailVo;
import com.dragon.backstage.domain.vo.menu.MenuVo;
import com.dragon.backstage.service.LoginService;
import com.dragon.backstage.service.MenuService;
import com.dragon.common.utils.AjaxResult;
import com.dragon.interfaces.constant.MenuConstant;
import com.dragon.interfaces.constant.UserConstant;
import com.dragon.interfaces.domain.request.menu.MenuRequest;
import com.dragon.interfaces.entity.Menu;
import com.dragon.interfaces.entity.RoleMenu;
import com.dragon.interfaces.mapper.MenuMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dragon.interfaces.mapper.RoleMenuMapper;
import com.google.common.collect.Lists;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @author dragon
 * @since 2024-08-01 18:05:09
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Resource
    private MenuMapper menuMapper;
    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Resource
    private LoginService loginService;


    /**
     * 菜单列表
     *
     * @param menuParam 菜单列表请求参数
     * @return 菜单列表
     */
    @Override
    public AjaxResult<List<MenuVo>> menuList(MenuParam menuParam) {
        List<MenuVo> menuVos = new ArrayList<>();

        List<Menu> menuList = menuMapper.selectMenuList(MenuConvert.toMenuRequest(menuParam));
        if (CollectionUtils.isNotEmpty(menuList)) {
            menuVos = menuList.stream().map(MenuConvert::toMenuVo).collect(Collectors.toList());
        }

        return AjaxResult.succeedWith(menuVos);
    }

    /**
     * 前端菜单路由列表
     *
     * @return 前端菜单路由列表
     */
    @Override
    public AjaxResult<List<MenuAsyncRouteVo>> menuAsyncRoutes() {
        List<Menu> menuList = new ArrayList<>();

        Long userId = loginService.getUserId();
        String account = loginService.getAccount();

        MenuRequest menuRequest = MenuRequest
            .builder()
            .menuTypes(Lists.newArrayList(MenuConstant.MENU_TYPE_DIRECTORY, MenuConstant.MENU_TYPE_MENU))
            .status(MenuConstant.STATUS_NORMAL)
            .build();
        if (UserConstant.SUPER_ADMIN_ACCOUNT.equals(account)) {
            // 管理员获取所有菜单
            menuList = menuMapper.selectMenuList(menuRequest);
        } else {
            menuRequest.setUserId(userId);
            menuList = menuMapper.selectUserPossessMenuList(menuRequest);
        }

        List<MenuAsyncRouteVo> menuAsyncRouteVos = buildTreeMenu(menuList, 0L);

        return AjaxResult.succeedWith(menuAsyncRouteVos);
    }

    /**
     * 前端用户权限列表
     *
     * @return 用户权限列表
     */
    @Override
    public AjaxResult<List<MenuAsyncPermissionVo>> menuAsyncPermissions() {
        List<Menu> menuList = new ArrayList<>();
        List<MenuAsyncPermissionVo> menuAsyncPermissions = new ArrayList<>();

        Long userId = loginService.getUserId();
        String account = loginService.getAccount();

        MenuRequest menuRequest = MenuRequest
            .builder()
            .hasPermission(Boolean.TRUE)
            .status(MenuConstant.STATUS_NORMAL)
            .build();
        if (UserConstant.SUPER_ADMIN_ACCOUNT.equals(account)) {
            // 管理员获取所有权限
            menuList = menuMapper.selectMenuList(menuRequest);
        } else {
            menuRequest.setUserId(userId);
            // 获取用户拥有的权限
            menuList = menuMapper.selectUserPossessMenuList(menuRequest);
        }

        if (CollectionUtils.isNotEmpty(menuList)) {
            menuAsyncPermissions = menuList.stream().map(MenuConvert::toMenuAsyncPermissionVo).collect(Collectors.toList());
        }

        return AjaxResult.succeedWith(menuAsyncPermissions);
    }

    /**
     * 菜单详情
     *
     * @param menuId 菜单ID
     * @return 菜单详情
     */
    @Override
    public AjaxResult<MenuDetailVo> menuDetail(Long menuId) {
        if (Objects.isNull(menuId) || menuId.equals(0L)) {
            return AjaxResult.failed("菜单ID不能为空");
        }
        Menu menu = menuMapper.selectById(menuId);
        if (Objects.isNull(menu)) {
            return AjaxResult.failed("菜单不存在");
        }

        return AjaxResult.succeedWith(MenuConvert.toMenuDetailVo(menu));
    }

    /**
     * 添加菜单
     *
     * @param param 添加菜单请求参数
     * @return 添加结果
     */
    @Override
    public AjaxResult<Void> addMenu(MenuAddParam param) {
        AjaxResult<Void> check = MenuAddParam.check(param);
        if (!check.getIsSuccessful()) {
            return AjaxResult.failed(check.getMessage());
        }

        // 判断菜单名单是否存在
        boolean existMenuName = menuMapper.exists(new QueryWrapper<Menu>()
            .eq(Menu.MENU_NAME, param.getMenuName())
        );
        if (existMenuName) {
            return AjaxResult.failed("菜单名称【" + param.getMenuName() + "】已存在");
        }

        // 判断路由名称是否存在
        if (StringUtils.isNoneBlank(param.getName())) {
            boolean existName = menuMapper.exists(new QueryWrapper<Menu>()
                .eq(Menu.NAME, param.getName())
            );
            if (existName) {
                return AjaxResult.failed("路由名称【" + param.getName() + "】已存在");
            }
        }

        boolean flag = menuMapper.insert(Menu
            .builder()
            .parentId(param.getParentId())
            .menuName(param.getMenuName())
            .menuSort(param.getMenuSort())
            .menuType(param.getMenuType())
            .visible(param.getVisible())
            .status(param.getStatus())
            .permission(param.getPermission())
            .remark(param.getRemark())

            .name(param.getName())
            .path(param.getPath())
            .component(param.getComponent())
            .icon(param.getIcon())
            .isFull(param.getIsFull())
            .activeMenu(param.getActiveMenu())
            .redirect(param.getRedirect())
            .isLink(param.getIsLink())

            .delFlag(MenuConstant.DEL_NO)
            .build()
        ) > 0;

        if (!flag) {
            return AjaxResult.failed("菜单添加失败");
        }

        return AjaxResult.succeed();
    }

    /**
     * 编辑菜单
     *
     * @param param 编辑菜单请求参数
     * @return 编辑结果
     */
    @Override
    public AjaxResult<Void> editMenu(MenuEditParam param) {
        AjaxResult<Void> check = MenuEditParam.check(param);
        if (!check.getIsSuccessful()) {
            return AjaxResult.failed(check.getMessage());
        }

        if (unExistByMenuId(param.getMenuId())) {
            return AjaxResult.failed("菜单不存在");
        }

        // 判断菜单名单是否存在
        boolean existMenuName = menuMapper.exists(new QueryWrapper<Menu>()
            .eq(Menu.MENU_NAME, param.getMenuName())
            .ne(Menu.MENU_ID, param.getMenuId())
        );
        if (existMenuName) {
            return AjaxResult.failed("菜单名称【" + param.getMenuName() + "】已存在");
        }

        // 判断路由名称是否存在
        if (StringUtils.isNoneBlank(param.getName())) {
            boolean existName = menuMapper.exists(new QueryWrapper<Menu>()
                .eq(Menu.NAME, param.getName())
                .ne(Menu.MENU_ID, param.getMenuId())
            );
            if (existName) {
                return AjaxResult.failed("路由名称【" + param.getName() + "】已存在");
            }
        }

        boolean flag = menuMapper.updateById(Menu
            .builder()
            .menuId(param.getMenuId())

            .parentId(param.getParentId())
            .menuName(param.getMenuName())
            .menuSort(param.getMenuSort())
            .menuType(param.getMenuType())
            .visible(param.getVisible())
            .status(param.getStatus())
            .permission(param.getPermission())
            .remark(param.getRemark())

            .name(param.getName())
            .path(param.getPath())
            .component(param.getComponent())
            .icon(param.getIcon())
            .isFull(param.getIsFull())
            .activeMenu(param.getActiveMenu())
            .redirect(param.getRedirect())
            .isLink(param.getIsLink())
            .build()
        ) > 0;

        if (!flag) {
            return AjaxResult.failed("菜单编辑失败");
        }

        return AjaxResult.succeed();
    }

    /**
     * 删除菜单
     *
     * @param menuId 菜单ID
     * @return 删除结果
     */
    @Override
    public AjaxResult<Void> deleteMenu(Long menuId) {
        if (Objects.isNull(menuId) || menuId.equals(0L)) {
            return AjaxResult.failed("菜单ID不能为空");
        }
        if (unExistByMenuId(menuId)) {
            return AjaxResult.failed("菜单不存在");
        }
        // 判断菜单有子菜单
        boolean hasChild = menuMapper.exists(new QueryWrapper<Menu>()
            .eq(Menu.PARENT_ID, menuId)
        );
        if (hasChild) {
            return AjaxResult.failed("存在子菜单,不允许删除");
        }
        // 判断菜单是否被分配
        boolean existsRoleFlag = roleMenuMapper.exists(new QueryWrapper<RoleMenu>()
            .eq(RoleMenu.MENU_ID, menuId)
        );
        if (existsRoleFlag) {
            return AjaxResult.failed("菜单已分配角色，无法删除");
        }

        boolean flag = menuMapper.deleteById(menuId) > 0;
        if (!flag) {
            return AjaxResult.failed("菜单删除失败");
        }

        return AjaxResult.succeed();
    }

    /**
     * 更改菜单显示状态
     *
     * @param param 更改菜单显示状态请求参数
     * @return 更改结果
     */
    @Override
    public AjaxResult<Void> changeVisible(MenuChangeVisibleParam param) {
        AjaxResult<Void> check = MenuChangeVisibleParam.check(param);
        if (!check.getIsSuccessful()) {
            return AjaxResult.failed(check.getMessage());
        }

        if (unExistByMenuId(param.getMenuId())) {
            return AjaxResult.failed("菜单不存在");
        }

        boolean flag = menuMapper.updateById(Menu
            .builder()
            .menuId(param.getMenuId())
            .visible(param.getVisible())
            .build()
        ) > 0;
        if (!flag) {
            return AjaxResult.failed("菜单显示状态更改失败");
        }

        return AjaxResult.succeed();
    }

    /**
     * 更改菜单状态
     *
     * @param param 更改菜单状态请求参数
     * @return 更改结果
     */
    @Override
    public AjaxResult<Void> changeStatus(MenuChangeStatusParam param) {
        AjaxResult<Void> check = MenuChangeStatusParam.check(param);
        if (!check.getIsSuccessful()) {
            return AjaxResult.failed(check.getMessage());
        }

        if (unExistByMenuId(param.getMenuId())) {
            return AjaxResult.failed("菜单不存在");
        }

        boolean flag = menuMapper.updateById(Menu
            .builder()
            .menuId(param.getMenuId())
            .status(param.getStatus())
            .build()
        ) > 0;
        if (!flag) {
            return AjaxResult.failed("菜单状态更改失败");
        }

        return AjaxResult.succeed();
    }

    /**
     * 生成菜单树形结构
     *
     * @param menuList 菜单列表
     * @param parentId 父菜单ID
     * @return 树形菜单
     */
    private List<MenuAsyncRouteVo> buildTreeMenu(List<Menu> menuList, Long parentId) {
        List<MenuAsyncRouteVo> menuAsyncRouteVos = new ArrayList<>();
        for (Menu menu : menuList) {
            MenuAsyncRouteVo menuAsyncRouteVo = MenuAsyncRouteVo
                .builder()
                .name(menu.getName())
                .path(menu.getPath())
                .component(menu.getComponent())
                .build();

            menuAsyncRouteVo.setMeta(MenuAsyncRouteVo.MetaInfo
                .builder()
                .title(menu.getMenuName())
                .icon(menu.getIcon())
                .activeMenu(StringUtils.isBlank(menu.getActiveMenu()) ? StringUtils.EMPTY : menu.getActiveMenu())
                .isLink(StringUtils.EMPTY)
                .isHide(MenuConstant.VISIBLE_HIDDEN.equals(menu.getVisible()))
                .isFull(MenuConstant.FULL_YES.equals(menu.getIsFull()))
                .isAffix(MenuConstant.AFFIX_YES.equals(menu.getIsAffix()))
                .isKeepAlive(MenuConstant.KEEP_ALIVE_YES.equals(menu.getIsKeepAlive()))
                .build()
            );

            if (menu.getParentId().equals(parentId)) {
                menuAsyncRouteVo.setChildren(buildTreeMenu(menuList, menu.getMenuId()));
                menuAsyncRouteVos.add(menuAsyncRouteVo);
            }
        }

        return menuAsyncRouteVos;
    }

    /**
     * 根据菜单ID判断是否不存在
     *
     * @param menuId 菜单ID
     * @return boolean
     */
    private boolean unExistByMenuId(Long menuId) {
        return !menuMapper.exists(new QueryWrapper<Menu>()
            .eq(Menu.MENU_ID, menuId)
        );
    }

}
